﻿using System.Collections.Generic;

using UnityEngine;

namespace YuoTools
{
    public static class MathfExtension
    {
        /// <summary>
        /// 只有返回值不修改原值
        /// </summary>
        /// <param name="i"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static float RClamp(this float i, float min, float max)
        {
            return Mathf.Clamp(i, min, max);
        }

        public static float RClamp(this float i, float max)
        {
            return Mathf.Clamp(i, 0, max);
        }

        public static float RClamp(this float i)
        {
            return Mathf.Clamp(i, 0, i);
        }

        public static int RClamp(this int i, int min, int max)
        {
            return Mathf.Clamp(i, min, max);
        }

        public static int RClamp(this int i, int max)
        {
            return Mathf.Clamp(i, 0, max);
        }

        public static int RClamp(this int i)
        {
            return Mathf.Clamp(i, 0, i);
        }

        public static int Clamp(ref this int i, int min, int max)
        {
            i = Mathf.Clamp(i, min, max);
            return i;
        }

        public static int Clamp(ref this int i, int max)
        {
            i = Mathf.Clamp(i, 0, max);
            return i;
        }

        public static int Clamp(ref this int i)
        {
            i = Mathf.Clamp(i, 0, i);
            return i;
        }

        public static float Clamp(ref this float i, float min, float max)
        {
            i = Mathf.Clamp(i, min, max);
            return i;
        }

        public static float Clamp(ref this float i, float max)
        {
            i = Mathf.Clamp(i, 0, max);
            return i;
        }

        public static float Clamp(ref this float i)
        {
            i = Mathf.Clamp(i, 0, i);
            return i;
        }

        public static float Abs(ref this float i)
        {
            i = Mathf.Abs(i);
            return i;
        }

        public static float RAbs(this float i)
        {
            return Mathf.Abs(i);
        }

        /// <summary>
        /// 交换两个数字
        /// </summary>
        /// <param name="f1"></param>
        /// <param name="f2"></param>
        public static void Swap(ref this float f1, ref float f2)
        {
            Temp.Float = f1;
            f1 = f2;
            f2 = Temp.Float;
        }

        /// <summary>
        /// 判断是否在范围内
        /// </summary>
        public static bool InRange(this float i, float min, float max)
        {
            return i >= min && i <= max;
        }

        /// <summary>
        /// 判断是否在范围内
        /// </summary>
        public static bool InRange(this int i, int min, int max)
        {
            return i >= min && i <= max;
        }

        /// <summary>
        /// 判断是否在范围内
        /// </summary>
        public static bool InRange(this Vector3 vector, Vector3 OtherVector, float x, float y, float z)
        {
            if (Mathf.Abs(vector.x - OtherVector.x) > x)
                return false;
            if (Mathf.Abs(vector.y - OtherVector.y) > y)
                return false;
            if (Mathf.Abs(vector.z - OtherVector.z) > z)
                return false;
            return true;
        }

        /// <summary>
        /// 判断是否在范围内
        /// </summary>
        public static bool InRange(this Vector2 vector, Vector2 OtherVector, float x, float y)
        {
            if (Mathf.Abs(vector.x - OtherVector.x) > x)
                return false;
            if (Mathf.Abs(vector.y - OtherVector.y) > y)
                return false;
            return true;
        }

        /// <summary>
        /// 如果传入为0,则输出0.000001f
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        public static float NonZero(this float i)
        {
            return i.ApEqual(0, 0.000001f) ? 0.000001f : i;
        }

        /// <summary>
        /// 判断是否近似相等
        /// </summary>
        /// <param name="value"></param>
        /// <param name="target"></param>
        /// <param name="accuracy">精确度</param>
        /// <returns></returns>
        public static bool ApEqual(this float value, float target, float accuracy = 0.01f)
        {
            if (Mathf.Abs(value - target) < accuracy)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 将一个bool变量变为相反的值
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool Reverse(ref this bool b)
        {
            b = !b;
            return b;
        }

        /// <summary>
        /// 返回一个随机的bool值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="random"></param>
        /// <returns></returns>
        public static bool RandomBool<T>(this T random)
        {
            return UnityEngine.Random.Range(0, 2).Equals(0);
        }

        /// <summary>
        /// 返回一个随机的二维向量
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static Vector3 RandomPos2D(float x, float y)
        {
            return Temp.V3.SetPos(UnityEngine.Random.Range(-x, x), UnityEngine.Random.Range(-y, y), 0);
        }

        /// <summary>
        /// 三维向量相乘
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static Vector3 Multiply(this Vector3 a, Vector3 b)
        {
            return Temp.V3.SetPos(a.x * b.x, a.y * b.y, a.z * b.z);
        }

        /// <summary>
        /// 乘方
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static float PowTwo(this float p)
        {
            return Mathf.Pow(p, 2);
        }

        /// <summary>
        /// 乘方
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static int PowTwo(this int p)
        {
            return (int)Mathf.Pow(p, 2);
        }

        /// <summary>
        /// float从一个区间映射到另一个区间
        /// </summary>
        /// <param name="In">需要映射的值</param>
        /// <param name="InMin">原区间最小值</param>
        /// <param name="InMax">原区间最大值</param>
        /// <param name="OutMin">目标区间最小值</param>
        /// <param name="OutMax">目标区间最大值</param>
        /// <returns></returns>
        public static float Remap(this float In, float InMin, float InMax, float OutMin, float OutMax)
        {
            return (In - InMin) * (OutMax - OutMin) / (InMax - InMin) + OutMin;
        }

        /// <summary>
        /// int从一个区间映射到另一个区间
        /// </summary>
        /// <param name="In">需要映射的值</param>
        /// <param name="InMin">原区间最小值</param>
        /// <param name="InMax">原区间最大值</param>
        /// <param name="OutMin">目标区间最小值</param>
        /// <param name="OutMax">目标区间最大值</param>
        /// <returns></returns>
        public static int Remap(this int In, int InMin, int InMax, int OutMin, int OutMax)
        {
            return (In - InMin) * (OutMax - OutMin) / (InMax - InMin) + OutMin;
        }

        /// <summary>
        /// 判断变量是否与另一组变量集合有交集
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="objs"></param>
        /// <returns></returns>
        public static bool EqualsOr<T>(this T obj, T[] objs)
        {
            foreach (var item in objs)
            {
                if (object.Equals(item, obj))
                {
                    return true;
                }
            }
            return false;
        }

        ///<summary>
        ///用最小二乘法拟合二元多次曲线
        ///例如y=ax+b
        ///其中MultiLine将返回a，b两个参数。
        ///a对应MultiLine[1]
        ///b对应MultiLine[0]
        ///</summary>
        ///<param name="arrX">已知点的x坐标集合</param>
        ///<param name="arrY">已知点的y坐标集合</param>
        ///<param name="length">已知点的个数</param>
        ///<param name="dimension">方程的最高次数</param>
        public static float[] MultiLine(float[] arrX, float[] arrY, int length, int dimension)
        {
            int n = dimension + 1;                  //dimension次方程需要求 dimension+1个 系数
            float[,] Guass = new float[n, n + 1];      //高斯矩阵 例如：y=a0+a1*x+a2*x*x
            for (int i = 0; i < n; i++)
            {
                int j;
                for (j = 0; j < n; j++)
                {
                    Guass[i, j] = SumPowerArr(arrX, j + i, length);
                }
                Guass[i, j] = SumPowerArr(arrX, i, arrY, 1, length);
            }

            return ComputGauss(Guass, n);
        }

        /// <summary>
        /// 求数组的元素的n次方的和
        /// </summary>
        /// <param name="arr"></param>
        /// <param name="power"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private static float SumPowerArr(float[] arr, int power, int length)
        {
            float s = 0;
            for (int i = 0; i < length; i++)
            {
                if (arr[i] != 0 || power != 0)
                    s = s + Mathf.Pow(arr[i], power);
                else
                    s = s + 1;
            }
            return s;
        }

        /// <summary>
        ///  求数组的元素的n次方的乘积的和
        /// </summary>
        /// <param name="arr1"></param>
        /// <param name="power1"></param>
        /// <param name="arr2"></param>
        /// <param name="power2"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private static float SumPowerArr(float[] arr1, int power1, float[] arr2, int power2, int length)
        {
            float s = 0;
            for (int i = 0; i < length; i++)
            {
                if ((arr1[i] != 0 || power1 != 0) && (arr2[i] != 0 || power2 != 0))
                    s = s + Mathf.Pow(arr1[i], power1) * Mathf.Pow(arr2[i], power2);
                else
                    s = s + 1;
            }
            return s;
        }

        /// <summary>
        /// 返回函数的系数
        /// </summary>
        /// <param name="Guass"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private static float[] ComputGauss(float[,] Guass, int n)
        {
            int i, j;
            int k, m;
            float temp;
            float max;
            float s;
            float[] x = new float[n];

            for (i = 0; i < n; i++) x[i] = 0;//初始化

            for (j = 0; j < n; j++)
            {
                max = 0;

                k = j;
                for (i = j; i < n; i++)
                {
                    if (Mathf.Abs(Guass[i, j]) > max)
                    {
                        max = Guass[i, j];
                        k = i;
                    }
                }

                if (k != j)
                {
                    for (m = j; m < n + 1; m++)
                    {
                        temp = Guass[j, m];
                        Guass[j, m] = Guass[k, m];
                        Guass[k, m] = temp;
                    }
                }

                if (0 == max)
                {
                    // "此线性方程为奇异线性方程"

                    return x;
                }

                for (i = j + 1; i < n; i++)
                {
                    s = Guass[i, j];
                    for (m = j; m < n + 1; m++)
                    {
                        Guass[i, m] = Guass[i, m] - Guass[j, m] * s / (Guass[j, j]);
                    }
                }
            }//结束for (j=0;j<n;j++)

            for (i = n - 1; i >= 0; i--)
            {
                s = 0;
                for (j = i + 1; j < n; j++)
                {
                    s = s + Guass[i, j] * x[j];
                }

                x[i] = (Guass[i, n] - s) / Guass[i, i];
            }

            return x;
        }

        /// <summary>
        /// 计算一堆三维坐标点中距离最远的线段(近似,适合位置数量较多的情况)
        /// </summary>
        /// <param name="pointer"></param>
        /// <returns></returns>
        public static Line GetMaxLineAp(this List<Vector3> pointer)
        {
            Vector3 Left = Vector3.zero;
            Vector3 Right = Vector3.zero;
            Vector3 Up = Vector3.zero;
            Vector3 down = Vector3.zero;
            Vector3 Front = Vector3.zero;
            Vector3 Back = Vector3.zero;
            foreach (var item in pointer)
            {
                if (item.x < Left.x)
                {
                    Left = item;
                }
                if (item.x > Right.x)
                {
                    Right = item;
                }
                if (item.y < down.y)
                {
                    down = item;
                }
                if (item.y > Up.y)
                {
                    Up = item;
                }
                if (item.z < Back.z)
                {
                    Back = item;
                }
                if (item.z > Front.z)
                {
                    Front = item;
                }
            }
            List<Vector3> temp = new List<Vector3>();
            temp.Add(Left);
            temp.Add(Right);
            temp.Add(Up);
            temp.Add(down);
            temp.Add(Front);
            temp.Add(Back);
            return GetMaxLine(temp);
        }

        /// <summary>
        /// 计算一堆三维坐标点中距离最远的线段(近似,适合位置数量较多的情况)
        /// </summary>
        /// <param name="pointer"></param>
        /// <returns></returns>
        public static LineTran GetMaxLineAp(this List<Transform> pointer)
        {
            Transform Left = pointer[0];
            Transform Right = pointer[0];
            Transform Up = pointer[0];
            Transform down = pointer[0];
            Transform Front = pointer[0];
            Transform Back = pointer[0];
            foreach (var item in pointer)
            {
                if (item.position.x < Left.position.x)
                {
                    Left = item;
                }
                if (item.position.x > Right.position.x)
                {
                    Right = item;
                }
                if (item.position.y < down.position.y)
                {
                    down = item;
                }
                if (item.position.y > Up.position.y)
                {
                    Up = item;
                }
                if (item.position.z < Back.position.z)
                {
                    Back = item;
                }
                if (item.position.z > Front.position.z)
                {
                    Front = item;
                }
            }
            List<Transform> temp = new List<Transform>();
            temp.Add(Left);
            temp.Add(Right);
            temp.Add(Up);
            temp.Add(down);
            temp.Add(Front);
            temp.Add(Back);
            return GetMaxLine(temp);
        }

        /// <summary>
        /// 计算一堆三维坐标点中距离最远的线段
        /// </summary>
        /// <param name="pointer"></param>
        /// <returns></returns>
        public static Line GetMaxLine(this List<Vector3> pointer)
        {
            Vector3 start = Vector3.zero;
            Vector3 end = Vector3.zero;
            float lenth = 0;
            for (int x = 0; x < pointer.Count; x++)
            {
                for (int y = pointer.Count - 1; y >= 0; y--)
                {
                    float l = Vector3.Distance(pointer[x], pointer[y]);
                    if (l > lenth)
                    {
                        l = lenth;
                        start = pointer[x];
                        end = pointer[y];
                    }
                }
            }
            return new Line() { start = start, end = end, lenth = lenth };
        }

        /// <summary>
        /// 计算一堆三维坐标点中距离最远的线段
        /// </summary>
        /// <param name="pointer"></param>
        /// <returns></returns>
        public static LineTran GetMaxLine(this List<Transform> pointer)
        {
            Transform start = pointer[0];
            Transform end = pointer[0];
            float lenth = 0;
            for (int x = 0; x < pointer.Count; x++)
            {
                for (int y = pointer.Count - 1; y >= 0; y--)
                {
                    float l = Vector3.Distance(pointer[x].position, pointer[y].position);
                    if (l > lenth)
                    {
                        l = lenth;
                        start = pointer[x];
                        end = pointer[y];
                    }
                }
            }
            return new LineTran() { start = start, end = end, lenth = lenth };
        }
    }

    public struct Line
    {
        public Vector3 start;
        public Vector3 end;
        public float lenth;
    }

    public struct LineTran
    {
        public Transform start;
        public Transform end;
        public float lenth;
    }
}